﻿namespace Sai.Cecil
{
	using Sai.Core.Introspection;
	using Mono.Cecil;

	/// <summary>
	/// Factory type extension to build a declarations from equivalent mono definition objects.
	/// </summary>
	public static class MethodDefinitionExtensions
	{
		/// <summary>
		/// Creates a method declaration from the method definition.
		/// </summary>
		/// <param name="method">The method.</param>
		/// <returns>The method object.</returns>
		public static MethodDeclaration ToMethodDeclaration(this MethodDefinition method)
		{
			var publicMethod = new MethodDeclaration(
											method.Name,
											method.ReturnType.ToTypeName(),
											method.BuildObsoleteMarker(),
											method.DeclaringType.ToTypeName())
			{
				Signature = method.Signature(),
                IsAbstract = method.IsAbstract && !method.DeclaringType.IsInterface,
                IsProtected = method.IsProtected(),
                IsStatic = method.IsStatic
			};

			return publicMethod;
		}

		/// <summary>
		/// Is this method suitable for an interface declaration ?.
		/// </summary>
		/// <param name="method">The method.</param>
		/// <returns>True if it is an interface method.</returns>
		public static bool IsInterfaceMethod(this MethodDefinition method)
		{
			if (method.IsSpecialName || method.IsConstructor)
			{
				return false;
			}

			return method.IsPublic;
		}

		/// <summary>
		/// Creates a constructor declaration from a method definition and type reference.
		/// </summary>
		/// <param name="method">The method.</param>
		/// <param name="discoveredType">The enclosing type.</param>
		/// <returns>The constructor object.</returns>
		public static InstanceConstructorDeclaration ToConstructorDeclaration(this MethodDefinition method, TypeReference discoveredType)
		{
			var constructor = new InstanceConstructorDeclaration(
														discoveredType.ToTypeName(),
														method.BuildObsoleteMarker())
			{
				Signature = method.Signature()
			};

			return constructor;
		}

        /// <summary>
        /// Creates a static constructor declaration from a method definition and type reference.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <param name="discoveredType">The enclosing type.</param>
        /// <returns>The static constructor object.</returns>
        public static StaticConstructorDeclaration ToStaticConstructorDeclaration(this MethodDefinition method, TypeReference discoveredType)
        {
            return new StaticConstructorDeclaration(
                                                    discoveredType.ToTypeName(),
                                                    method.BuildObsoleteMarker());
        }

        public static FinalizerDeclaration ToFinalizerDeclaration(this MethodDefinition method, TypeReference discoveredType)
        {
            return new FinalizerDeclaration(discoveredType.ToTypeName());
        }

		/// <summary>
		/// Creates a signature object appropriate from the method definition.
		/// </summary>
		/// <param name="method">The method.</param>
		/// <returns>The signature object.</returns>
		private static Signature Signature(this MethodDefinition method)
		{
			var signature = new Signature(method.Name);

			if (method.HasParameters)
			{
				foreach (var parameter in method.Parameters)
				{
					signature.Add(new Parameter(parameter.ParameterType.ToTypeName()));
				}
			}

			return signature;
		}

        public static bool IsIncluded(this MethodDefinition method)
        {
            return method.IsPublic || method.IsProtected();
        }

        public static bool IsProtected(this MethodDefinition method)
        {
            return method.IsFamily || method.IsFamilyAndAssembly || method.IsFamilyOrAssembly;
        }

        public static bool IsFinalizer(this MethodDefinition method)
        {
            return method.Name == "Finalize" && method.Parameters.Count == 0;
        }
	}
}
